package com.Jano.servlet;
import com.Jano.dto.Result;
import com.Jano.exception.BusinessException;
import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import static com.Jano.constant.BusinessConstant.MEDIA_TYPE_JSON;

/**
 * 对Servlet操作的封装
 * 1、统一设置了跨域消息头
 * 2、对于路径的获取做了统一获取，路径获取后作为doService方法的参数，在自己的Servlet类中
 * 继承这个类以后只要实现其中的doService方法，其中的参数就是当前请求的路径，可以直接判断是否为对应操作
 * 3、统一对dao->service层逐层抛出的异常做了统一处理（除了Servlet层以外的每一层都不处理异常，都是向上抛出了异常）
 * service不再需要包装成Result对象，而只需要关注最后需要返回给前端什么样的有效数据，对于success、errMsg、data属性
 * 的封装操作在这里统一完成，doService中的具体逻辑为调用Service层中的方法，然后得到方法的返回值，
 * 这里根据调用后的结果进行包装，如果service层方法调用后返回的是null或者返回的就是null，以及service中出现异常
 * 则不在data中设置任何值，另外出现异常后，Result中success设置为false并给定对应的errMsg错误信息
 * 4、对service层调用后的结果统一封装为Result对象以后统一在这里通过JSON格式返回给前端
 */
public abstract class AbstractBaseServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        String uri = req.getRequestURI();
        System.out.println(uri);
        String action = uri.substring(uri.lastIndexOf("/") + 1);
        resp.setContentType(MEDIA_TYPE_JSON);
        PrintWriter pw = resp.getWriter();
        Result result = new Result();
        try {
            Object obj = doService(action, req, resp);
            result.setData(obj);
        } catch (BusinessException e) {
            result.setSuccess(false);
            result.setErrMsg(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrMsg("服务器异常！");
        }
        pw.print(JSON.toJSONString(result, true));
    }

    /**
     * 这步操作中的doService方法的逻辑要看具体Servlet中的实现
     * 因为当前类中这个方法是抽象方法，具体逻辑是交给其子类去实现的
     * @param action
     * @param req
     * @param res
     * @return
     * @throws UnsupportedEncodingException
     */
    public abstract Object doService(String action, HttpServletRequest req, HttpServletResponse res) throws UnsupportedEncodingException;

    /**
     * 将请求参数对应的Map集合转换成对应的JavaBean对象
     * Map中的key和JavaBean中的同名属性对应
     * @param req 请求对象
     * @param clazz 要转换出来的JavaBean类型
     * @return 请求参数Map转换出来的JavaBean对象
     */
    protected <T> T paramMap2Bean(HttpServletRequest req, Class<T> clazz) {
        try {
            T t = clazz.getDeclaredConstructor().newInstance();
            BeanUtils.populate(t, req.getParameterMap());
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("参数格式有误");
        }
    }
}
